package global

import (
	"context"
	"fmt"
	"log"
	"time"

	"web/pkg/setting"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

// 实体模型:具有唯一标识的对象，通常对应业务中的核心概念;----值对象:与其相反没有唯一标识的对象(对应描述实体的属性如购物车的参数:数量,金额)
type Entity struct {
	ID        int `gorm:"primaryKey" json:"id"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}
type BaseModel struct {
	Entity //实体模型
}

func Setup() error {
	var err error
	// 构造 DSN
	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local",
		setting.DatabaseSetting.User,
		setting.DatabaseSetting.Password,
		setting.DatabaseSetting.Host,
		setting.DatabaseSetting.Name)
	// 自定义命名策略
	customNamingStrategy := schema.NamingStrategy{
		TablePrefix:   setting.DatabaseSetting.TablePrefix, // 表前缀
		SingularTable: false,                               // 是否开启表前缀
	}
	// 使用新的 GORM 方法打开数据库连接，并应用自定义命名策略
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: customNamingStrategy,
	})

	if err != nil {
		return fmt.Errorf("models.Setup err: %v", err)
	}

	// 设置连接池参数
	sqlDB, err := DB.DB()
	if err != nil {
		return fmt.Errorf("Failed to get underlying sql.DB instance: %v", err)
	}
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)

	// 注册回调
	DB.Callback().Create().Before("gorm:create").Register("update_time_stamp", updateTimeStampForCreateCallback)
	DB.Callback().Update().Before("gorm:update").Register("update_time_stamp", updateTimeStampForUpdateCallback)
	DB.Callback().Delete().Before("gorm:delete").Register("delete", deleteCallback)
	
	return nil
}

// CloseDB closes database connection
func CloseDB() {
	sqlDB, err := DB.DB()
	if err != nil {
		log.Printf("Failed to get underlying sql.DB instance: %v", err)
		return
	}
	sqlDB.Close()
}

// updateTimeStampForCreateCallback will set `CreatedAt`, `UpdatedAt` when creating
func updateTimeStampForCreateCallback(db *gorm.DB) {
	if db.Statement.Schema != nil {
		nowTime := time.Now().Unix()
		if field := db.Statement.Schema.LookUpField("CreatedAt"); field != nil {
			field.Set(context.Background(), db.Statement.ReflectValue, nowTime)
		}
		if field := db.Statement.Schema.LookUpField("UpdatedAt"); field != nil {
			field.Set(context.Background(), db.Statement.ReflectValue, nowTime)
		}
	}
}

// updateTimeStampForUpdateCallback will set `UpdatedAt` when updating
func updateTimeStampForUpdateCallback(db *gorm.DB) {
	if db.Statement.Schema != nil {
		if field := db.Statement.Schema.LookUpField("UpdatedAt"); field != nil {
			field.Set(context.Background(), db.Statement.ReflectValue, time.Now().Unix())
		}
	}
}

// deleteCallback will set `DeletedAt` where deleting
func deleteCallback(db *gorm.DB) {
	if db.Statement.Schema != nil {
		if field := db.Statement.Schema.LookUpField("DeletedAt"); field != nil {
			field.Set(context.Background(), db.Statement.ReflectValue, time.Now().Unix())
		}
	}
}
